package dingtalk

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"gitee.com/kvenli/go-sender/core"
	"gitee.com/kvenli/go-sender/dingtalk/response"
)

const (
	Timeout = 5 * time.Second
	Webhook = "https://oapi.dingtalk.com/robot/send"
)

type Robot struct {
	Token, Secret string
	Client        *http.Client
}

// New 创建一个新的钉钉机器人实例。
func New(token, secret string) *Robot {
	return &Robot{
		Token:  token,
		Secret: secret,
		Client: &http.Client{Timeout: Timeout}, // 直接初始化 HTTP 客户端
	}
}

// Send 发送消息到钉钉机器人。
func (bot *Robot) Send(msg core.Message) (*core.Response, error) {
	// 钉钉消息类型通常是具体的结构体，例如 message.Text, message.Markdown 等
	// 这里我们直接使用 core.Message，并在内部进行 JSON 序列化
	// 如果需要对特定消息类型进行额外处理，可以在这里进行类型断言

	var (
		data []byte
		err  error
	)
	if data, err = json.Marshal(msg); err != nil {
		core.GetLogger().Printf("[%s] Send error: %s", msg.GetType(), err.Error())
		return nil, fmt.Errorf("%w: %v", core.ErrSendMessageFailed, err)
	}

	core.GetLogger().Printf("[%s] Sending message to DingTalk: %s", msg.GetType(), string(data))
	var result *core.Response
	if result, err = bot.send(data); err != nil {
		core.GetLogger().Printf("[%s] Send error: %s", msg.GetType(), err.Error())
		return nil, err
	}

	core.GetLogger().Printf("[%s] Message sent to DingTalk. Response Code: %d, Message: %s", msg.GetType(), result.Code, result.Message)
	return result, nil
}

// send 执行 HTTP 请求发送消息。
func (bot *Robot) send(data []byte) (*core.Response, error) {
	var (
		req *http.Request
		err error
	)
	if req, err = bot.buildRequest(data, http.Header{}); err != nil {
		return nil, fmt.Errorf("%w: %v", core.ErrNetworkFailure, err)
	}

	var resp *http.Response
	if resp, err = bot.Client.Do(req); err != nil {
		return nil, fmt.Errorf("%w: %v", core.ErrNetworkFailure, err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%w: HTTP 错误码: %d", core.ErrNetworkFailure, resp.StatusCode)
	}

	var body []byte
	if body, err = ioutil.ReadAll(resp.Body); err != nil {
		return nil, fmt.Errorf("%w: %v", core.ErrNetworkFailure, err)
	}

	// 将钉钉的原始响应转换为 core.Response
	dingtalkResp := new(response.Response)
	if err = json.Unmarshal(body, &dingtalkResp); err != nil {
		return nil, fmt.Errorf("%w: %v", core.ErrNetworkFailure, err)
	}

	coreResp := &core.Response{
		Code:    dingtalkResp.Code,
		Message: dingtalkResp.Message,
	}

	// 处理钉钉特有的状态码和信息
	if dingtalkResp.Status > 0 {
		coreResp.Code = dingtalkResp.Status
		coreResp.Message = fmt.Sprintf("punish[%s] wait[%d]", dingtalkResp.Punish, dingtalkResp.Wait)
	}

	return coreResp, nil
}

// buildRequest 构建 HTTP POST 请求。
func (bot *Robot) buildRequest(data []byte, header http.Header) (*http.Request, error) {
	var (
		url *url.URL
		err error
	)
	if url, err = bot.buildWebHook(); err != nil {
		return nil, err
	}

	var req *http.Request
	if req, err = http.NewRequest("POST", url.String(), bytes.NewReader(data)); err != nil {
		return nil, err
	}

	header.Set("Content-Type", "application/json;charset=utf-8")
	req.Header = header

	return req, nil
}

// buildWebHook 构建机器人 Webhook URL。
func (bot *Robot) buildWebHook() (*url.URL, error) {
	var (
		url *url.URL
		err error
	)
	if url, err = url.Parse(Webhook); err != nil {
		return nil, err
	}

	query := url.Query()
	query.Set("access_token", bot.Token)
	if bot.Secret != "" {
		timestamp, sign := bot.sign()
		query.Add("timestamp", strconv.FormatInt(timestamp, 10))
		query.Add("sign", sign)
	}
	url.RawQuery = query.Encode()

	return url, nil
}

// sign 生成请求签名。
func (bot *Robot) sign() (int64, string) {
	timestamp := time.Now().Unix() * 1000
	src := fmt.Sprintf("%d\n%s", timestamp, bot.Secret)

	sha256 := hmac.New(sha256.New, []byte(bot.Secret))
	sha256.Write([]byte(src))

	return timestamp, base64.StdEncoding.EncodeToString(sha256.Sum(nil))
}
